﻿namespace Softserve.Pottery.Infrastructure.Serialization
{
    using System;
    using System.Linq;
    using System.Collections.Generic;
    using System.Data.SqlTypes;
    using System.IO;
    using System.Runtime.Serialization;
    using System.Xml;
    using System.Xml.Linq;
    using System.Xml.Serialization;
    using Softserve.Pottery.Infrastructure.Interfaces;
    using System.Text;

    /// <summary>
    /// Contains operations for working with Xml storage
    /// </summary>
    public static class XmlController
    {
        /// <summary>
        /// Writes collection to end of the Xml file
        /// </summary>
        /// <typeparam name="T">Type</typeparam>
        /// <param name="newRecords">Collection to write</param>
        public static void AddCollection<T>(IEnumerable<T> newRecords)
        {
            if (newRecords == null)
            {
                throw new ArgumentNullException("newRecords is null");
            }
            PathResolver pr = new PathResolver();
            XDocument doc = new XDocument();
            using (var writer = doc.CreateWriter())
            {
                List<T> lst = LoadCollection<T>().ToList();
                lst.AddRange(newRecords);
                var serializer = new DataContractSerializer(lst.GetType());
                serializer.WriteObject(writer, lst);
            }
            doc.Save(pr.ResolvePath(typeof(T).Name));
        }

        /// <summary>
        /// Writes domain to XML
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="newRecord"></param>
        public static void AddDomain<T>(T newRecord)
        {
            if (newRecord == null)
            {
                throw new ArgumentNullException("newRecords is null");
            }
            PathResolver pr = new PathResolver();
            XDocument doc = new XDocument();
            using (var writer = doc.CreateWriter())
            {
                List<T> lst = LoadCollection<T>().ToList();
                lst.Add(newRecord);
                var serializer = new DataContractSerializer(lst.GetType());
                serializer.WriteObject(writer, lst);
            }
            doc.Save(pr.ResolvePath(typeof(T).Name));
        }

        /// <summary>
        /// Rewrites XML file with new collection
        /// </summary>
        /// <typeparam name="T">Domain</typeparam>
        /// <param name="newRecords">Collection for recording</param>
        public static void ReWriteCollection<T>(IEnumerable<T> newRecords)
        {
            if (newRecords == null)
            {
                throw new ArgumentNullException("newRecords is null");
            }
            PathResolver pr = new PathResolver();
            XDocument doc = new XDocument();
            using (var writer = doc.CreateWriter())
            {
                List<T> lst = new List<T>();
                lst.AddRange(newRecords);
                var serializer = new DataContractSerializer(lst.GetType());
                serializer.WriteObject(writer, lst);
            }
            doc.Save(pr.ResolvePath(typeof(T).Name));
        }

        /// <summary>
        /// Reads collection from Xml storage
        /// </summary>
        /// <typeparam name="T">Type</typeparam>
        /// <returns>Collection from file</returns>
        public static IEnumerable<T> LoadCollection<T>()
        {
            PathResolver pr = new PathResolver();
            var ds = new DataContractSerializer(typeof(T[]));
            var settings = new XmlReaderSettings { };
            T[] arr = new T[] { };
            using (var r = XmlReader.Create((pr.ResolvePath(typeof(T).Name))))
            {
                arr = ds.ReadObject(r) as T[];
            }
            return arr;
        }

        /// <summary>
        /// Gets 
        /// </summary>
        /// <typeparam name="T">IDomain</typeparam>
        /// <param name="idToFind">Id of domain</param>
        /// <returns>Domain result</returns>
        public static T GetDomainById<T>(int idToFind) where T : class, IDomain
        {
            IEnumerable<T> lst = LoadCollection<T>();
            var res =
                (from item in lst
                 where item.Id == idToFind
                 select item).FirstOrDefault<T>();
            return res;
        }

        /// <summary>
        /// Replaces older domain by the new one
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="newDomain"></param>
        public static void UpdateDomain<T>(T newDomain) where T : class, IDomain
        {
            List<T> list = LoadCollection<T>().ToList();
            var q = list.Where(x => x.Id == newDomain.Id).FirstOrDefault();
            list[list.IndexOf(q)] = newDomain;
            ReWriteCollection<T>(list);
        }


        /// <summary>
        /// Remove domain of selected type by index
        /// </summary>
        /// <typeparam name="T">Domain type</typeparam>
        /// <param name="id">Index</param>
        public static void RemoveDomain<T>(int id) where T : class, IDomain
        {
            List<T> list = LoadCollection<T>().ToList();
            var q = list.Where(x => x.Id == id).FirstOrDefault();
            if (list.IndexOf(q) != -1)
            {
                list.RemoveAt(list.IndexOf(q));
            }
            ReWriteCollection<T>(list);
        }

        /// <summary>
        /// Converts object to XElement type
        /// </summary>
        /// <typeparam name="T">Some object type</typeparam>
        /// <param name="obj">Some object</param>
        /// <returns>XElement</returns>
        public static XElement ToXElement<T>(object obj)
        {
            using (var memoryStream = new MemoryStream())
            {
                using (TextWriter streamWriter = new StreamWriter(memoryStream))
                {
                    var xmlSerializer = new XmlSerializer(typeof(T));
                    xmlSerializer.Serialize(streamWriter, obj);
                    return XElement.Parse(Encoding.ASCII.GetString(memoryStream.ToArray()));
                }
            }
        }
    }
}
